' Heightmap demo 4
'
' Vertex lighting
'
const heightScale# = 10 ' Height map scale factor
const texScale# = 0.1   ' Number of textures per grid square
dim xSize, ySize
dim xMask, yMask        ' bitmasks to apply to X and Y coordinates
dim file                ' File handle
dim x, y                ' Working variables
dim angle#              ' Viewing angle (used for animation)
dim texture             ' OpenGL texture handle for our texture
dim tx#, ty#            ' Texture coordinates
dim a#(2), b#(2), c#(2), d#(2), norm#(2) ' Temporary vectors used for lighting
dim intensity#

goto Start

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Routines
CheckError:
    ' Check for file error
    if FileError () <> "" then
        print FileError ()
        CloseFile (file)
        end
    endif
    return

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Main program stars here    
Start:

' Open file
file = OpenFileRead ("Files\Hills01.dat")
    ' Note: Basic4GL will only open files in the "files" subfolder of the folder
    '       where the program is saved.
gosub CheckError

' Read X and Y size
xSize = Val (ReadLine (file))
ySize = Val (ReadLine (file))
gosub CheckError

' Now that size is known, we can dim our 2D array
dim h#(xSize - 1)(ySize - 1)        
xMask = xSize - 1
yMask = ySize - 1

for y = 0 to ySize - 1
    for x = 0 to xSize - 1
        h#(x)(y) = val (ReadText (file, true)) * heightScale#
        gosub CheckError
    next
next          
CloseFile (file)

' Create light map
' This will be a 2D grid stating the light intensity at each heightmap point, as a
' value between 0 (fully dark) to 1 (fully lit)
' To calculate the light map, we need to have a light source, to define the direction
' of the light.
dim lightSource#(2)
lightSource# = vec3(0, -1, 0)           ' Light shines straight down.
' (Try changing the light source direction for different effects).

lightSource# = Normalize (lightSource#) ' Light source must be length 1

' Create light map
dim l# (xSize - 1)(ySize - 1)
for x = 0 to xSize - 1
    for y = 0 to ySize - 1
        ' A . B
        ' . P .
        ' C . D
        
        ' To calculate the lighting at point P, we cross product the vectors (D-A)
        ' and (B-C) and normalize, to give the normal at point P.
        ' We then dot product the result with the negated light vector.
        ' This will give us a value where 1 = light shining directly onto surface,
        ' and 0 (or below) = surface pointing away from the light.
        ' We raise it to the power of 3 to give a slightly specular effect, and
        ' clamp it at 0.2 to apply an ambient light level.
        a# = vec3 (x,   h#(x)(y),                             y)
        b# = vec3 (x+1, h#((x+1) and xMask)(y),               y)
        c# = vec3 (x  , h#(x              )((y+1) and yMask), y+1)        
        d# = vec3 (x+1, h#((x+1) and xMask)((y+1) and yMask), y+1)

        norm# = Normalize (CrossProduct (d# - a#, b# - c#))        

        intensity# = norm# * -lightSource#

        intensity# = intensity# * intensity# * intensity#
        if intensity# < 0.2 then intensity# = 0.2 endif
            
        l#(x)(y) = intensity#
    next
next

' Load texture file 
texture = LoadMipmapTexture ("textures\00005.jpg")

' Enable texture mapping, and bind our texture
glEnable (GL_TEXTURE_2D)
glBindTexture (GL_TEXTURE_2D, texture)

while true
    
    ' Clear the screen
    glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
        
    ' Setup the transformations
    glLoadIdentity ()                       ' Clear out any existing transformations
    glTranslatef (0, 0, -50)                ' "push" map away
    glRotatef (20, 1, 0, 0)                 ' Elevation rotation
    glRotatef (angle#, 0, 1, 0)             ' Animation rotation
    glTranslatef (-xSize/2, 0, -ySize/2)    ' Centre the map    

    ' Draw the heightmap points
    for x = 0 to xSize - 2
        for y = 0 to ySize - 2
        
            ' Calculate the texture coordinates
            tx# = x * texScale#
            ty# = y * texScale#

            ' Draw a grid square.
            ' Apply the lightmap, by setting the colour at each corner.
            ' We will multiply the colour white (1,1,1) as a vector by the light
            ' intensity. Then use the resulting vector as our colour coordinates
            glBegin (GL_QUADS)
                intensity# = l#(x)(y)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx#            , ty#)
                glVertex3f (x  , h#(x  )(y  ), y  )
                
                intensity# = l#(x+1)(y)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx# + texScale#, ty#)
                glVertex3f (x+1, h#(x+1)(y  ), y  )

                intensity# = l#(x+1)(y+1)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx# + texScale#, ty# + texScale#)
                glVertex3f (x+1, h#(x+1)(y+1), y+1)

                intensity# = l#(x)(y+1)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx#            , ty# + texScale#)
                glVertex3f (x  , h#(x  )(y+1), y+1)
            glEnd ()
        next
    next
    
    ' Show the result
    SwapBuffers ()
    
    ' Animation
    while SyncTimer (20)
        angle# = angle# + 0.8
    wend
wend